////////////////////////////////////////////////////////////////////////////////
/// LEDMatrix.c
/// Main source code for 32x16 LED matrix.
///
/// Hardware:
///   20MHz external crystal
///   6x 74hc595 shift register
///     1-2 LED Common Anodes?
///     3-6 LED Common Cathodes?
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// Hardware SPI Connections
///
/// /OE -> Ground
/// SER -> MOSI
/// SRCLK -> SCK
/// /SRCLR -> 5V
/// RCLK -> /SS
///
/// SER = Serial Data In
/// SRCLK = Shift Register Clock
////////////////////////////////////////////////////////////////////////////////

//////////////////////////
/// Define Statements  ///
//////////////////////////
//Pins
#define DATA_PIN 6
#define CLOCK_PIN 8
#define LATCH_PIN 5
//PORTB
#define DATA_PIN_PORTB (DATA_PIN - 1)
#define CLOCK_PIN_PORTB (CLOCK_PIN - 1)
#define LATCH_PIN_PORTB (LATCH_PIN - 1)
//Timer Prescalers
#define TIMER0_PRESCALER 256
#define TIMER1_PRESCALER 1024
//Frequencies
#define F_CPU 20000000 //20MHz
//Screen Refresh Timer Config
#define SCREEN_REFRESH_HERTZ 300
#define SCREEN_REFRESH_CYCLES (F_CPU / SCREEN_REFRESH_HERTZ)
#define SCREEN_REFRESH_INTERRUPT (F_CPU/TIMER0_PRESCALER/SCREEN_UPDATE_HERTZ -1)
//Screen Update Timer Config
#define SCREEN_UPDATE_HERTZ 1
#define SCREEN_UPDATE_CYCLES (F_CPU / SCREEN UPDATAE HERTZ)
#define SCREEN_UPDATE_INTERRUPT (F_CPU/TIMER1_PRESCALER/SCREEN_UPDATE_HERTZ -1)


//Target Timer Count = (Input Frequency / Prescale) / Target Frequency - 1 

//Sizes
#define HEIGHT 16
#define WIDTH 32
#define DATA_BYTES (HEIGHT / 8) * WIDTH

////////////////
/// Includes ///
////////////////
//Include the Header file for Input/Output Operations
#include <avr/io.h>
//Include the Header file for Interrupts
#include <avr/interrupt.h>
//Include library for Program Memory
#include <avr/pgmspace.h>
//Include software shift register library
#include <74HC595.h>
//Include font map


//////////////////////////
///Function Prototypes ///
//////////////////////////
//void softwareShiftOut(uint8_t data);
void copyProgramMem(uint8_t location);

///////////////
// Variables //	
///////////////
uint8_t data[DATA_BYTES];
uint8_t k = 0;
uint8_t indexLetter = 16;

prog_uint8_t characters[128][5] PROGMEM=
{
	{0x00,0x00,0x00,0x00,0x00}, //Blank 0
	{0x00,0x00,0x00,0x00,0x00}, //Blank 1
	{0x00,0x00,0x00,0x00,0x00}, //Blank 2
	{0x00,0x00,0x00,0x00,0x00}, //Blank 3
	{0x00,0x00,0x00,0x00,0x00}, //Blank 4
	{0x00,0x00,0x00,0x00,0x00}, //Blank 5
	{0x00,0x00,0x00,0x00,0x00}, //Blank 6
	{0x00,0x00,0x00,0x00,0x00}, //Blank 7
	{0x00,0x00,0x00,0x00,0x00}, //Blank 8
	{0x00,0x00,0x00,0x00,0x00}, //Blank 9
	{0x00,0x00,0x00,0x00,0x00}, //Blank 10
	{0x00,0x00,0x00,0x00,0x00}, //Blank 11
	{0x00,0x00,0x00,0x00,0x00}, //Blank 12
	{0x00,0x00,0x00,0x00,0x00}, //Blank 13
	{0x00,0x00,0x00,0x00,0x00}, //Blank 14
	{0x00,0x00,0x00,0x00,0x00}, //Blank 15
	{0x00,0x00,0x00,0x00,0x00}, //Blank 16
		
	{0x00,0x00,0x4F,0x00,0x00},	// !
	{0x00,0x07,0x00,0x07,0x00},	// "
	{0x14,0x7F,0x14,0x7F,0x14},	// #
	{0x24,0x2A,0x7F,0x2A,0x12},	// $
	{0x23,0x13,0x08,0x64,0x62},	// %
	{0x36,0x49,0x55,0x22,0x50},	// &
	{0x00,0x05,0x03,0x00,0x00},	// '
	{0x00,0x1C,0x22,0x41,0x00},	// (
	{0x00,0x41,0x22,0x1C,0x00},	// )
	{0x14,0x08,0x3E,0x08,0x14},	// *
	{0x08,0x08,0x3E,0x08,0x08},	// +
	{0x00,0x50,0x30,0x00,0x00},	// ,
	{0x08,0x08,0x08,0x08,0x08},	// -
	{0x00,0x60,0x60,0x00,0x00},	// .
	{0x20,0x10,0x08,0x04,0x02},	// /

	{0x3E,0x51,0x49,0x45,0x3E},	// 0
	{0x00,0x42,0x7F,0x40,0x00},	// 1
	{0x42,0x61,0x51,0x49,0x46},	// 2
	{0x21,0x41,0x45,0x4B,0x31},	// 3
	{0x18,0x14,0x12,0x7F,0x10},	// 4
	{0x27,0x45,0x45,0x45,0x39},	// 5
	{0x3C,0x4A,0x49,0x49,0x30},	// 6
	{0x01,0x71,0x09,0x05,0x03},	// 7
	{0x36,0x49,0x49,0x49,0x36},	// 8
	{0x06,0x49,0x49,0x29,0x1E},	// 9

	{0x36,0x36,0x00,0x00,0x00},	// :
	{0x56,0x36,0x00,0x00,0x00},	// ;
	{0x08,0x14,0x22,0x41,0x00},	// <
	{0x14,0x14,0x14,0x14,0x14},	// =
	{0x00,0x41,0x22,0x14,0x08},	// >
	{0x02,0x01,0x51,0x09,0x06},	// ?
	{0x30,0x49,0x79,0x41,0x3E},	// @

	{0x7E,0x11,0x11,0x11,0x7E},	// A
	{0x7F,0x49,0x49,0x49,0x36},	// B
	{0x3E,0x41,0x41,0x41,0x22},	// C
	{0x7F,0x41,0x41,0x22,0x1C},	// D
	{0x7F,0x49,0x49,0x49,0x41},	// E
	{0x7F,0x09,0x09,0x09,0x01},	// F
	{0x3E,0x41,0x49,0x49,0x7A},	// G
	{0x7F,0x08,0x08,0x08,0x7F},	// H
	{0x00,0x41,0x7F,0x41,0x00},	// I
	{0x20,0x40,0x41,0x3F,0x01},	// J
	{0x7F,0x08,0x14,0x22,0x41},	// K
	{0x7F,0x40,0x40,0x40,0x40},	// L
	{0x7F,0x02,0x0C,0x02,0x7F},	// M
	{0x7F,0x04,0x08,0x10,0x7F},	// N
	{0x3E,0x41,0x41,0x41,0x3E},	// O
	{0x7F,0x09,0x09,0x09,0x06},	// P
	{0x3E,0x41,0x51,0x21,0x5E},	// Q
	{0x7F,0x09,0x19,0x29,0x46},	// R
	{0x46,0x49,0x49,0x49,0x31},	// S
	{0x01,0x01,0x7F,0x01,0x01},	// T
	{0x3F,0x40,0x40,0x40,0x3F},	// U
	{0x1F,0x20,0x40,0x20,0x1F},	// V
	{0x3F,0x40,0x30,0x40,0x3F},	// W
	{0x63,0x14,0x08,0x14,0x63},	// X
	{0x07,0x08,0x70,0x08,0x07},	// Y
	{0x61,0x51,0x49,0x45,0x43},	// Z

	{0x00,0x7F,0x41,0x41,0x00},	// [
	{0x02,0x04,0x08,0x10,0x20},	// backslash
	{0x00,0x41,0x41,0x7F,0x00},	// ]
	{0x04,0x02,0x01,0x02,0x04},	// ^
	{0x40,0x40,0x40,0x40,0x40},	// _
	{0x00,0x01,0x02,0x04,0x00},	// `

	{0x20,0x54,0x54,0x54,0x78},	// a
	{0x7F,0x50,0x48,0x48,0x30},	// b
	{0x38,0x44,0x44,0x44,0x20},	// c
	{0x38,0x44,0x44,0x48,0x7F},	// d
	{0x38,0x54,0x54,0x54,0x18},	// e
	{0x08,0x7E,0x09,0x01,0x02},	// f
	{0x0C,0x52,0x52,0x52,0x3E},	// g
	{0x7F,0x08,0x04,0x04,0x78},	// h
	{0x00,0x44,0x7D,0x40,0x00},	// i
	{0x20,0x40,0x44,0x3D,0x00},	// j
	{0x7F,0x10,0x28,0x44,0x00},	// k
	{0x00,0x41,0x7F,0x40,0x00},	// l
	{0x78,0x04,0x58,0x44,0x78},	// m
	{0x7C,0x08,0x04,0x04,0x78},	// n
	{0x38,0x44,0x44,0x44,0x38},	// o
	{0x7C,0x14,0x14,0x14,0x08},	// p
	{0x08,0x14,0x14,0x18,0x7C},	// q
	{0x7C,0x08,0x04,0x04,0x08},	// r
	{0x48,0x54,0x54,0x54,0x20},	// s
	{0x04,0x3F,0x44,0x40,0x20},	// t
	{0x3C,0x40,0x40,0x20,0x7C},	// u
	{0x1C,0x20,0x40,0x20,0x1C},	// v
	{0x3C,0x40,0x30,0x40,0x3C},	// w
	{0x44,0x28,0x10,0x28,0x44},	// x
	{0x0C,0x50,0x50,0x50,0x3C},	// y
	{0x44,0x64,0x54,0x4C,0x44},	// z

	{0x00,0x08,0x36,0x41,0x00},	// {
	{0x00,0x00,0x7F,0x00,0x00},	// |
	{0x00,0x41,0x36,0x08,0x00},	// }
	{0x0C,0x02,0x0C,0x10,0x0C},	// ~

	{0x00,0x00,0x00,0x00,0x00}
};

int main(void) { 
	//////////////////////
	/// Initialization ///
	//////////////////////
	DDRB |= 255; //Set PORTB for Output (using bit-OR statement)
	DDRD |= 255; //Set PORTD for Output (using bit-OR statement)
	
	//////////////
	/// Timer0 ///
	//////////////
	//Configure Timer Mode: CTC
	TCCR0A |= (1 << WGM01);
	//Enable CTC interrupt
	TIMSK0 |= (1 << OCIE0A);
	//Set Prescaler to 256
	TCCR0B |=(1 << CS02);
	//Set compare value
	OCR0A = 100;

	//////////////
	/// Timer1 ///
	//////////////
	//Configure Timer Mode: CTC
	TCCR1B |= (1 << WGM12);
	//Enable CTC Interrupt
	TIMSK1 |= (1 << OCIE1A);
	//Set Compare Value for ~1Hz
	OCR1A = 19000;
	//Timer control register
	TCCR1B |= ((1 << CS10) | (1 << CS12)); //timer with fOSC/1024 scaler

	//Enable global Interrupt
	sei();

	////////////
	/// Loop ///
	////////////
	for (;;) { //Infinite Loop
	     ;
	}
}

////////////////////////////////////////////////////////////////////////////////
/// Interrupt Service Routine Timer0
/// This interrupt manages displaying the data onto the screen. It pulls the
/// current frame from the data array and shifts it out using softwareShiftOut()
/// The interrupt fires about 
////////////////////////////////////////////////////////////////////////////////
ISR(TIMER0_COMPA_vect) {
	// Variables
	int i = 0, foo = 0;

	//Loop through 8 columns (8 bits of data)
	for (i = 0; i < 8; i++)
		// Extract data by first shifting it k number of rows, then AND'ing it with
		// the ones place, then shifting it i number of columns to build output var
		// foo to shift out columns
		foo |= (((data[i] >> k) & 0x01) << i );

	//Shift 8 bits of data out 74hc595 register
	softwareShiftOut(foo);

	//increment column variable for next time
	k++;
	//only 8 rows so far
	if (k > 8)
		k = 0;
}

////////////////////////////////////////////////////////////////////////////////
///Interrupt Service Routine Timer1
////////////////////////////////////////////////////////////////////////////////
ISR(TIMER1_COMPA_vect) {
	copyProgramMem(&characters[indexLetter][0]);

	indexLetter++;
}



////////////////////////////////////////////////////////////////////////////////
/// void copyProgramMem
/// reads program memory 5 bytes at a time.
/// copies to global array data from 0 to 4
////////////////////////////////////////////////////////////////////////////////
void copyProgramMem(uint8_t location) {
	int i;
	for (i = 0; i < 5; i++) {
		data[i] = pgm_read_byte_near(location + i);
	}
}



